/* 
 * Copyright (c) 2009-2012, Newcastle University, UK.
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are met: 
 * 1. Redistributions of source code must retain the above copyright notice, 
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice, 
 *    this list of conditions and the following disclaimer in the documentation 
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 * POSSIBILITY OF SUCH DAMAGE. 
 */
 
// usb_cdc.c - USB CDC device code.
// Portions derived from Microchip Technology file 'main.c' from "USB Device - Composite - CDC" example.


// Interrupt or polling method.  If using polling, must call
// USBDeviceTasks() periodically.  This function will take care
// of processing and responding to SETUP transactions 
// (such as during the enumeration process when you first
// plug in).  USB hosts require that USB devices should accept
// and process SETUP packets in a timely fashion.  Therefore,
// when using polling, this function should be called 
// frequently (such as once about every 100 microseconds) at any
// time that a SETUP packet might reasonably be expected to
// be sent by the host to your device.  In most cases, the
// USBDeviceTasks() function does not take very long to
// execute (~50 instruction cycles) before it returns.


// Includes
#include <USB/USB.h>
#include <stdio.h>
#include <TimeDelay.h>
#include <USB/usb_function_cdc.h>
#include "HardwareProfile.h"
#include "USB_CDC.h"


// Variables
#pragma idata

static char usbDebugStatus = 0;

// USB circular buffer head/tail pointers
unsigned short inHead = 0, inTail = 0;
unsigned short outHead = 0, outTail = 0;


#pragma udata

// USB circular buffers
//#define IN_BUFFER_CAPACITY_POWER  8     // 2^8 = 256
//#define OUT_BUFFER_CAPACITY_POWER 8     // 2^8 = 256
//#define IN_BUFFER_CAPACITY (((short)1)<<IN_BUFFER_CAPACITY_POWER)
//#define OUT_BUFFER_CAPACITY (((short)1)<<OUT_BUFFER_CAPACITY_POWER)
//#if 1
//#define OUT_MASK(_i) ((_i) & (OUT_BUFFER_CAPACITY - 1))
//#define IN_MASK(_i)  ((_i) & (IN_BUFFER_CAPACITY - 1))
//#else
//#define OUT_MASK(_i) ((_i) % OUT_BUFFER_CAPACITY)
//#define IN_MASK(_i)  ((_i) % IN_BUFFER_CAPACITY)
//#endif
unsigned char inBuffer[IN_BUFFER_CAPACITY] = {0};
unsigned char outBuffer[OUT_BUFFER_CAPACITY] = {0};


// Private prototypes
void USBDeviceTasks(void);
void USBDebugStatus(void);


// Declarations
#pragma code

/*
// Circular buffer operations (b = buffer, h = head, t = tail, i = index / number of elements, c = capacity)
static unsigned char _buffer_temp;
#define BUFFER_CLEAR(_h, _t) { _h = 0; _t = 0; }
#define BUFFER_INCREMENT(_i, _c) (((_i) + 1) % (_c))
#define BUFFER_ADD(_b, _h, _t, _c, _v) { if (BUFFER_INCREMENT(_t, _c) != (_h)) { (_b)[_t] = (_v); _t = BUFFER_INCREMENT(_t, _c); } }
#define BUFFER_REMOVE(_b, _h, _t, _c) (((_h) == (_t)) ? 0 : (_buffer_temp = (_b)[_h], _h = BUFFER_INCREMENT(_h, _c), _buffer_temp))
#define BUFFER_LENGTH(_h, _t, _c) (((_t) >= (_h)) ? ((_t) - (_h)) : ((_c) - (_h) + (_t)))
#define BUFFER_FREE(_h, _t, _c) (((_t) >= (_h)) ? ((_c) - (_t) + (_h) - 1) : ((_h) - (_t) - 1))
#define BUFFER_CONTIGUOUS_ENTRIES(_h, _t, _c) (((_t) >= (_h)) ? ((_t) - (_h)) : ((_c) - (_h)))
#define BUFFER_ENTRY_POINTER(_b, _h) ((_b) + (_h))
#define BUFFER_CONTIGUOUS_FREE(_h, _t, _c) (((_t) >= (_h)) ? ((_c) - (_t) - ((_h) == 0 ? 1 : 0)) : ((_h) - (_t) - 1))
#define BUFFER_FREE_POINTER(_b, _t) ((_b) + (_t))
#define BUFFER_EXTERNALLY_ADDED(_t, _c, _i) ((_t) = (((_t) + (_i)) % (_c)))
#define BUFFER_EXTERNALLY_REMOVED(_h, _c, _i) ((_h) = (((_h) + (_i)) % (_c)))
*/


// InitializeSystem - Centralized initialization routine.
void USBInitializeSystem(void)
{
//    CLOCK_USB_XTAL();            // Initialize HS crystal and PLL for 48 MHz USB module to run at full-speed

#ifdef USE_USB_BUS_SENSE_IO
    USB_BUS_SENSE_PIN = 1;
#endif
    
    // Clear USB CDC I/O buffers
    inHead = 0; inTail = 0;
    outHead = 0; outTail = 0;
    USBDeviceInit();    //usb_device.c.  Initializes USB module SFRs and firmware variables to known states.
}


// USB-specific put char
void usb_putchar(unsigned char v)
{
    static unsigned short newOutTail;
    // Find new tail position
    newOutTail = OUT_MASK(outTail + 1);

    // If the queue is not full...
    if (newOutTail != outHead)
    {
        // Add the character at the tail, and increment the tail position
        outBuffer[outTail] = v;
        outTail = newOutTail;
    }
}


// USB-specific get char
int usb_getchar(void)
{
    int v = -1;
    // If the buffer is not empty...
    if (inHead != inTail)
    {
        // Remove the head item and increment the head
        v = inBuffer[inHead];
        inHead = IN_MASK(inHead + 1);
    }

	
    return v;
}

// USB-specific get char returns how many bytes are ready to read
unsigned char usb_getchars_ready(void)
{
    unsigned char v = 0;
    // If the buffer is not empty...
    if (inHead != inTail)
    {
        // Remove the head item and increment the head
        v = inHead - inTail;
        v = IN_MASK(v); // incase it had overflowed and tail > head
    }

    return v;
}

// USB write
unsigned char usb_write(void *buffer, unsigned char length)
{
    unsigned char *sp;                          // source pointer
    unsigned char *dp;                          // destination pointer
    unsigned short contiguous;                  // number of contiguous entries that could be written
    unsigned char remaining;                    // number of bytes left to write

    sp = (unsigned char *)buffer;               // source pointer at start of buffer
    remaining = length;                         // all buffer bytes still to write
    while (remaining > 0)                       // while there are still bytes to write...
    {
        dp = outBuffer + outTail;               // calculate destination pointer for this contiguous stretch; then calculate contiguous size
        contiguous = ((outTail >= outHead) ? (OUT_BUFFER_CAPACITY - outTail - (outHead == 0 ? 1 : 0)) : (outHead - outTail - 1));
        if (contiguous == 0) { break; }         // destination at capacity, cannot send any more, abort loop
        if (contiguous > remaining) { contiguous = remaining; } // clamp to number of bytes we'd actually want to add anyway
        remaining -= contiguous;                // adjust remaining count
        outTail = OUT_MASK(outTail + contiguous); // adjust tail pointer
        for(; contiguous > 0; contiguous--)     // while there are bytes to write
        {
            *dp++ = *sp++;                      // write byte, increment pointers
        }
    }
    return (length - remaining);                // return number of bytes written
}

// USB write SLIP (RFC 1055) encoded data
unsigned char usb_slip_encoded(void *buffer, unsigned char length)
{
#if 1       // Faster simple version
    static unsigned char len;
    static unsigned char *sp;
    static unsigned char c;
    static unsigned char newOutTail;

    sp = buffer;
    len = length;
    while (len--)
    {
        c = *sp++;
        switch (c)
        {
            case SLIP_END:
                // Terminate if the queue is full, otherwise add the ESC character at the tail, and increment the tail position
                newOutTail = OUT_MASK(outTail + 1);
                if (newOutTail == outHead) { break; }    
                outBuffer[outTail] = SLIP_ESC;
                outTail = newOutTail; 
    
                // Escaped character code
                c = SLIP_ESC_END;
                break;

            case SLIP_ESC:
                // Terminate if the queue is full, otherwise add the ESC character at the tail, and increment the tail position
                newOutTail = OUT_MASK(outTail + 1);
                if (newOutTail == outHead) { break; }    
                outBuffer[outTail] = SLIP_ESC;
                outTail = newOutTail; 
    
                // Escaped character code
                c = SLIP_ESC_ESC;
                break;
        }

        // Terminate if the queue is full, otherwise add the character at the tail, and increment the tail position
        newOutTail = OUT_MASK(outTail + 1);
        if (newOutTail == outHead) { break; }    
        outBuffer[outTail] = c;
        outTail = newOutTail; 
    }
    return length;
#elif 1       // Simpler version
    unsigned char len;
    unsigned char *sp;

    sp = buffer;
    for (len = length; len != 0; len--)
    {
        switch (*sp)
        {
            case SLIP_END:
                usb_putchar(SLIP_ESC);
                usb_putchar(SLIP_ESC_END);
                break;
            case SLIP_ESC:
                usb_putchar(SLIP_ESC);
                usb_putchar(SLIP_ESC_ESC);
                break;
            default:
                usb_putchar(*sp);
        }
        ++sp;
    }
    return length;
#else       // Attempt at being faster, but probably isn't and is more complicated
    unsigned char *sp;                          // source pointer
    unsigned char *dp;                          // destination pointer
    int contiguous;                             // number of contiguous entries that could be written
    unsigned char remaining;                    // number of bytes left to write
    unsigned char count;                        // count of buffer bytes sent in each contiguous burst
    char lastEscape;                            // flag to indicate last written char was an escape
    unsigned char c;                            // byte being processed

    sp = (unsigned char *)buffer;               // source pointer at start of buffer
    remaining = length;                         // all buffer bytes still to write
    lastEscape = 0;                             // clear 'last char was escape' flag
    while (remaining > 0)                       // while there are still bytes to write...
    {
        dp = outBuffer + outTail;               // calculate destination pointer for this contiguous stretch; then calculate contiguous size
        contiguous = ((outTail >= outHead) ? (OUT_BUFFER_CAPACITY - outTail - (outHead == 0 ? 1 : 0)) : (outHead - outTail - 1));
        if (contiguous == 0) { break; }         // destination at capacity, cannot send any more, abort loop
        count = 0;                              // clear count of written bytes
        do                                      // loop
        {
            c = *sp;                            // read from source

            // Substitute the escaped characters (or write ESC if not yet written)
            if      (c == SLIP_END) { c = lastEscape ? SLIP_ESC_END : SLIP_ESC; }
            else if (c == SLIP_ESC) { c = lastEscape ? SLIP_ESC_ESC : SLIP_ESC; }

            *dp++ = c;                          // write to destination
            --contiguous;                       // one less byte in capacity

            if (c == SLIP_ESC)
            {
                lastEscape = 1;                 // set 'last char was escape' flag
            }
            else
            {
                lastEscape = 0;                 // clear 'last char was escape' flag
                sp++;                           // consume this character: increment source pointer
                count++;                        // consume this character: increment consumed count
            }

        } while (contiguous && count < remaining); // while still bytes to read and capacity to write
        remaining -= count;
        outTail = OUT_MASK(outTail + count);
    }
    return (length - remaining);
#endif
}


// Perform USB Serial I/O tasks -- must be called periodically from ProcessIO() to empty the out-buffer and fill the in- buffers
void USBSerialIO(void)
{
    unsigned char *buffer;
    unsigned char maxLength;
    unsigned char numBytes;

    // Receive USB CDC data
    buffer = inBuffer + inTail;
    maxLength = (inTail >= inHead) ? (IN_BUFFER_CAPACITY - inTail - (inHead == 0 ? 1 : 0)) : (inHead - 1 - inTail);
    if (maxLength > 0)
    {
        numBytes = getsUSBUSART((char *)buffer, maxLength);
        if (numBytes > 0)
        {
			inTail = IN_MASK(inTail + numBytes);
        }
    }
    
    // Transmit USB CDC data
    buffer = outBuffer + outHead;
    maxLength = (outTail >= outHead) ? (outTail - outHead) : (OUT_BUFFER_CAPACITY - outHead);
    if (maxLength > 0)
    {
	    if (maxLength > 64) { maxLength = 64; }
        if (USBUSARTIsTxTrfReady())
        {
            putUSBUSART((char *)buffer, maxLength);
            numBytes = maxLength;   // We have to assume that they were all written
            if (numBytes > 0)
            {
                outHead = OUT_MASK(outHead + numBytes);
            }
        }
    }
    
}


// USBProcessIO - User routines
void USBProcessIO(void)
{   
    // Blink the LEDs according to the USB device status
    if(usbDebugStatus)
    {
        USBDebugStatus();
    }

    //if ((USBDeviceState >= CONFIGURED_STATE) && (USBSuspendControl != 1))
	if((USBGetDeviceState() >= CONFIGURED_STATE) || (USBIsDeviceSuspended() == FALSE))      
    {
        USBSerialIO();
        CDCTxService();
    }
}


// Waits processing IO until buffer is empty
void USBCDCWait(void)
{
	// While configured and not suspended and buffer not empty, spin calling process I/O
    while ((USBDeviceState >= CONFIGURED_STATE) && (USBSuspendControl != 1) && outHead != outTail)
    {
        USBDeviceTasks(); 	// Interrupt or polling method.  If using polling, must call
		USBProcessIO();
    }
}


// USBDebugStatus - turns on and off LEDs corresponding to the USB device state.
void USBDebugStatus(void)
{
#if 1
    static unsigned short activity = 0;
    static unsigned short counter = 0;
//    static unsigned long lastReads = 0;
    char color;
    char flash;

    if (counter == 0) { counter = 20000U; } else { counter--; }
    flash = (counter < 5000) ? 1 : 0;
#ifdef BATT_NCHARGING
    color = BATT_NCHARGING ? 2 : 3;                                     // Charge indicator: GREEN = not charging (full); YELLOW = charging (not full)
#else
color = 3;
#endif

    if (!USBSuspendControl && USBDeviceState == ADDRESS_STATE)          // Address (~2-3 seconds when connecting)
    {
        color = (flash ? color : 4);                                    // Flash BLUE then (YELLOW|GREEN)
    }
    else if (!USBSuspendControl && USBDeviceState == CONFIGURED_STATE)  // Configured
    {
        color = (flash ? color : 7);                                    // Flash WHITE then (YELLOW|GREEN)
    }
    else
    {
        color = color;                                                  // YELLOW/GREEN = charge indicator
    }



    if (activity > 0) { activity--; }
    // TODO: Update 'activity' on activity
    if (activity && ((counter >> 9) & 1))
    {
//        LED_SET(0);
    }
    else
    {
//        LED_SET(color);
    }


#else
    static WORD led_count = 0;
    static char flash = 0;
    if (led_count == 0) { led_count = 10000U; flash = !flash; }
    led_count--;
    if      (USBSuspendControl)                                        { LED_SET(flash ? 3 : 0); }  // Flash YELLOW/OFF = suspend (~0.5 second once disconnected), when connected to a powered hub (previously fully connected)
    else if (!USBSuspendControl && USBDeviceState == DETACHED_STATE)   { LED_SET(4); }              // BLUE   = detached (when disconnected)
    else if (!USBSuspendControl && USBDeviceState == ATTACHED_STATE)   { LED_SET(3); }              // YELLOW = attached (briefly when connecting??)
    else if (!USBSuspendControl && USBDeviceState == POWERED_STATE)    { LED_SET(1); }              // RED    = powered  (briefly when connecting??)
    else if (!USBSuspendControl && USBDeviceState == DEFAULT_STATE)    { LED_SET(2); }              // GREEN  = default (briefly when connecting), when only connected to a powered hub (never connected)
    else if (!USBSuspendControl && USBDeviceState == ADDRESS_STATE)    { LED_SET(flash ? 1 : 0); }  // Flash RED/OFF = address (~2-3 seconds when connecting)
    else if (!USBSuspendControl && USBDeviceState == CONFIGURED_STATE) { LED_SET(flash ? 2 : 1); }  // Flash RED/GREEN = configured
    else                                                               { LED_SET(7); }              // (shouldn't happen)
#endif
}


#if defined(USB_CDC_SET_LINE_CODING_HANDLER)
// USBSetLineCodingHandler - called when a SetLineCoding command is sent on the bus.  
void USBSetLineCodingHandler(void)
{
    // Evaluate the request and determine whether the application should update the baudrate.
    if(cdc_notice.GetLineCoding.dwDTERate.Val > 115200)
    {
        // The request is not in a valid range, unsupported baud rate request
        // Stall the STATUS stage of the request (causes an exception in the requesting application).
        USBStallEndpoint(0, 1);
    }
    else
    {
        // Update the baudrate info in the CDC driver
        CDCSetBaudRate(cdc_notice.GetLineCoding.dwDTERate.Val);
    }
}
#endif


// USB callback functions
//
// The USB firmware stack will call the callback functions USBCBxxx() in response to certain USB related
// events.  For example, if the host PC is powering down, it will stop sending out Start of Frame (SOF)
// packets to your device.  In response to this, all USB devices are supposed to decrease their power
// consumption from the USB Vbus to <2.5mA each.  The USB module detects this condition (which according
// to the USB specifications is 3+ms of no bus activity/SOF packets) and then calls the USBCBSuspend()
// function.  You should modify these callback functions to take appropriate actions for each of these
// conditions.  For example, in the USBCBSuspend(), you may wish to add code that will decrease power
// consumption from Vbus to <2.5mA (such as by clock switching, turning off LEDs, putting the
// microcontroller to sleep, etc.).  Then, in the USBCBWakeFromSuspend() function, you may then wish to
// add code that undoes the power saving things done in the USBCBSuspend() function.
//
// The USBCBSendResume() function is special, in that the USB stack will not automatically call this
// function.  This function is meant to be called from the application firmware instead.  See the
// additional comments near the function.
//


// _USB1Interrupt - USB interrupt called when the USB interrupt bit is set.
// (Currently the interrupt is only used when the device goes to sleep when it receives a USB suspend command).
#if 0
void __attribute__ ((interrupt)) _USB1Interrupt(void)
{
    #if !defined(self_powered)
        if(U1OTGIRbits.ACTVIF)
        {
            IEC5bits.USB1IE = 0;
            U1OTGIEbits.ACTVIE = 0;
            IFS5bits.USB1IF = 0;
        
            //USBClearInterruptFlag(USBActivityIFReg, USBActivityIFBitNum);
            USBClearInterruptFlag(USBIdleIFReg, USBIdleIFBitNum);
            //USBSuspendControl = 0;
        }
    #endif
}
#endif


// USBCBSuspend - USB callback for when a USB suspend is detected.
void USBCBSuspend(void)
{
    //Example power saving code.  Insert appropriate code here for the desired
    //application behavior.  If the microcontroller will be put to sleep, a
    //process similar to that shown below may be used:
    
    //ConfigureIOPinsForLowPower();
    //SaveStateOfAllInterruptEnableBits();
    //DisableAllInterruptEnableBits();
    //EnableOnlyTheInterruptsWhichWillBeUsedToWakeTheMicro();    //should enable at least USBActivityIF as a wake source
    //Sleep();
    //RestoreStateOfAllPreviouslySavedInterruptEnableBits();    //Preferrably, this should be done in the USBCBWakeFromSuspend() function instead.
    //RestoreIOPinsToNormal();                                    //Preferrably, this should be done in the USBCBWakeFromSuspend() function instead.

    //IMPORTANT NOTE: Do not clear the USBActivityIF (ACTVIF) bit here.  This bit is 
    //cleared inside the usb_device.c file.  Clearing USBActivityIF here will cause 
    //things to not work as intended.    

    /*
    #if defined(__C30__)
        U1EIR = 0xFFFF;
        U1IR = 0xFFFF;
        U1OTGIR = 0xFFFF;
        IFS5bits.USB1IF = 0;
        IEC5bits.USB1IE = 1;
        U1OTGIEbits.ACTVIE = 1;
        U1OTGIRbits.ACTVIF = 1;
        Sleep();
    #endif
    */
}


// USBCBWakeFromSuspend - USB callback for wakeup from USB suspend.
void USBCBWakeFromSuspend(void)
{
    // The host may put USB peripheral devices in low power suspend mode (by "sending" 3+ms of idle).
    // Once in suspend mode, the host may wake the device back up by sending non-idle state signalling.
    
    // If clock switching or other power savings measures were taken when
    // executing the USBCBSuspend() function, now would be a good time to
    // switch back to normal full power run mode conditions.  The host allows
    // a few milliseconds of wakeup time, after which the device must be 
    // fully back to normal, and capable of receiving and processing USB
    // packets.  In order to do this, the USB module must receive proper
    // clocking (IE: 48MHz clock must be available to SIE for full speed USB
    // operation).
}


// USBCB_SOF_Handler - USB callback for SOF packet handling (full-speed devices every 1 ms).
void USBCB_SOF_Handler(void)
{
    // The USB host sends out a SOF packet to full-speed devices every 1 ms. 
    // This interrupt may be useful for isochronous pipes.
    
    // No need to clear UIRbits.SOFIF to 0 here (callback caller is already doing that).
}


// USBCBErrorHandler - USB callback for error handler.
void USBCBErrorHandler(void)
{
    // Mainly for debugging during development.
    // Check UEIR to see which error causes the interrupt.
    
    // No need to clear UEIR to 0 here.
    // Callback caller is already doing that.

    // Typically, user firmware does not need to do anything special
    // if a USB error occurs.  For example, if the host sends an OUT
    // packet to your device, but the packet gets corrupted (ex:
    // because of a bad connection, or the user unplugs the
    // USB cable during the transmission) this will typically set
    // one or more USB error interrupt flags.  Nothing specific
    // needs to be done however, since the SIE will automatically
    // send a "NAK" packet to the host.  In response to this, the
    // host will normally retry to send the packet again, and no
    // data loss occurs.  The system will typically recover
    // automatically, without the need for application firmware
    // intervention.
    
    // Nevertheless, this callback function is provided, such as
    // for debugging purposes.
}


// USBCBCheckOtherReq - USB callback to check other setup requests.
void USBCBCheckOtherReq(void)
{
    // When SETUP packets arrive from the host, some
    // firmware must process the request and respond
    // appropriately to fulfill the request.  Some of
    // the SETUP packets will be for standard
    // USB "chapter 9" (as in, fulfilling chapter 9 of
    // the official USB specifications) requests, while
    // others may be specific to the USB device class
    // that is being implemented.  For example, a HID
    // class device needs to be able to respond to
    // "GET REPORT" type of requests.  This
    // is not a standard USB chapter 9 request, and 
    // therefore not handled by usb_device.c.  Instead
    // this request should be handled by class specific 
    // firmware, such as that contained in usb_function_hid.c.
    USBCheckCDCRequest();
}


// USBCBStdSetDscHandler - USB callback for SETUP/SET_DESCRIPTOR request.
void USBCBStdSetDscHandler(void)
{
    // The USBCBStdSetDscHandler() callback function is
    // called when a SETUP, bRequest: SET_DESCRIPTOR request
    // arrives.  Typically SET_DESCRIPTOR requests are
    // not used in most applications, and it is
    // optional to support this type of request.
    
    // Must claim session ownership if supporting this request.
}


// USBCBInitEP - USB callback to initialize endpoints.
void USBCBInitEP(void)
{
    // This function is called when the device becomes
    // initialized, which occurs after the host sends a
    // SET_CONFIGURATION (wValue not = 0) request.  This 
    // callback function should initialize the endpoints 
    // for the device's usage according to the current 
    // configuration.
    CDCInitEP();
}


// USBCBSendResume - USB callback to wake host PC.
void USBCBSendResume(void)
{
    WORD delay_count;
    USBResumeControl = 1;                                       // Start RESUME signaling
    for (delay_count = 1800U; delay_count > 0; delay_count--);    // Set RESUME line for 1-13 ms    
    USBResumeControl = 0;
}


// USER_USB_CALLBACK_EVENT_HANDLER - USB callback event handler.
BOOL USER_USB_CALLBACK_EVENT_HANDLER(USB_EVENT event, void *pdata, WORD size)
{
    // Called from the USB stack to notify a user application that a USB event occured.
    // Switch on the type of event (pdata/size point to the event data).
    switch (event)
    {
        case EVENT_CONFIGURED: 
            USBCBInitEP();
            break;
        case EVENT_SET_DESCRIPTOR:
            USBCBStdSetDscHandler();
            break;
        case EVENT_EP0_REQUEST:
            USBCBCheckOtherReq();
            break;
        case EVENT_SOF:
            USBCB_SOF_Handler();
            break;
        case EVENT_SUSPEND:
            USBCBSuspend();
            break;
        case EVENT_RESUME:
            USBCBWakeFromSuspend();
            break;
        case EVENT_BUS_ERROR:
            USBCBErrorHandler();
            break;
        case EVENT_TRANSFER:
            Nop();
            break;
        default:
            break;
    }      
    return TRUE; 
}

